package lk.icta.core.web.action;

import lk.icta.common.AppConstants;
import lk.icta.common.dao.GNDivisionDAO;
import lk.icta.common.entity.DSDivision;
import lk.icta.common.web.action.BaseAction;
import lk.icta.core.entity.Land;
import lk.icta.core.entity.Schedule;
import lk.icta.core.service.LandManagementService;
import lk.icta.core.service.ScheduleManagementService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * @author Kusal Samarakoon
 */

public class ScheduleAction extends BaseAction {

    private static final Logger logger = LoggerFactory.getLogger(ScheduleAction.class);

    private final ScheduleManagementService scheduleManagementService;
    private final LandManagementService landManagementService;
    private final GNDivisionDAO gnDivisionDAO;

    private Map<Integer, String> gnDivisionList;
    private List<Schedule> scheduleList;

    private Schedule schedule;
    private int scheduleId;
    private long[] scheduleLandList;
    private int gnDivisionId;

    private String districtName;
    private String dsDivisionName;

    private final long MILLIS_IN_A_DAY = 1000 * 60 * 60 * 24;

    public ScheduleAction(ScheduleManagementService scheduleManagementService, LandManagementService landManagementService,
                          GNDivisionDAO gnDivisionDAO) {
        this.scheduleManagementService = scheduleManagementService;
        this.gnDivisionDAO = gnDivisionDAO;
        this.landManagementService = landManagementService;

    }

    public String pageLoadScheduleLandVisits() {
        logger.debug("Load schedules");
        populateBasicLists();
        viewScheduledLandVisits();
        return SUCCESS;
    }

    public String viewScheduledLandVisits() {
        logger.debug("Listing active schedules");
        try {
            scheduleList = scheduleManagementService.getAllActiveSchedulesByDSDivision(Schedule.Status.ACTIVE, user.getDsDivisions().iterator().next().getId(), user);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error(e.getMessage());
        }
        return SUCCESS;
    }

    protected void populateBasicLists() {
        try {
            logger.debug("Populate Basic Lists of schedule Details by {}", user.getUsername());
            populateDivisions();

        } catch (Exception e) {
            logger.debug("Error: ", e.getMessage());
            e.printStackTrace();
        }
    }

    public String scheduleLandVisitPlan() {
        logger.debug("Schedule Land visits by {}", getUser().getUsername());
        try {
            Date startDate = new Date(schedule.getEndDate().getTime() - MILLIS_IN_A_DAY * AppConstants.daysToReduce);
            List<Schedule> scheduleListFromDatabase = scheduleManagementService.getScheduleByDate(startDate);
            // Check whether at least one land is selected for schedule
            if (scheduleLandList == null || scheduleLandList.length < 1) {
                logger.error("No land has been selected for scheduling");
                addActionError(getText("no_lands_selected_for_schedule.label"));
                //clearFields();
                schedule = null;
                populateBasicLists();
                return SUCCESS;
            }
            Set<Land> scheduleLands = new HashSet<Land>();
            String landNames = "";
            Land scheduleLand;
            for (long id : scheduleLandList) {
                scheduleLand = landManagementService.getLandById(id);
                if (!isAllreadyScheduled(scheduleListFromDatabase, scheduleLand))
                    scheduleLands.add(scheduleLand);
                landNames += ", " + scheduleLand.getNameOfTheLand();
            }
            if (scheduleLands.size() > 0) {
                schedule.setStartDate(startDate);
                schedule.setLandList(scheduleLands);
                schedule.setGnDivision(gnDivisionDAO.getGNDivisionByPK(gnDivisionId));
                schedule.setStatus(Schedule.Status.ACTIVE);
                scheduleManagementService.addSchedule(schedule, getUser());
                addActionMessage(getText("scheduled_land_visits_of.label") + landNames);
                //clearFields();
                schedule = null;
                populateBasicLists();
                viewScheduledLandVisits();
                return SUCCESS;
            } else {
                addActionError(getText("already_scheduled.label"));
            }
        } catch (Exception e) {
            logger.error(e.getMessage());
            e.printStackTrace();
            addActionError(getText("unable_to_schedule.label"));
            return ERROR;
        }
        return ERROR;
    }

    private boolean isAllreadyScheduled(List<Schedule> schedule, Land land) {
        Set<Land> scheduledLands;
        for (int i = 0; i < schedule.size(); i++) {
            scheduledLands = schedule.get(i).getLandList();
            for (Land landInDatabase : scheduledLands) {
                if (landInDatabase.getId() == land.getId())
                    return true;
            }
        }
        return false;
    }

    public String deleteScheduleLandVisits() {
        try {
            Schedule schedule = scheduleManagementService.getScheduleById(scheduleId);
            schedule.setStatus(Schedule.Status.INACTIVE);
            scheduleManagementService.updateSchedule(schedule, user);
            scheduleList = scheduleManagementService.getAllActiveSchedule(Schedule.Status.ACTIVE);
            populateBasicLists();
            addActionMessage(getText("schedule_deleted_successfully.label"));
            return SUCCESS;

        } catch (Exception e) {
            logger.error(e.getMessage());
            e.printStackTrace();
            addActionError(getText("unable_to_delete_schedule.label"));
            return ERROR;

        }

    }

    protected void populateDivisions() {
        if (!user.getDsDivisions().isEmpty()) {
            DSDivision userDS = user.getDsDivisions().iterator().next();  //find the DS division of DS user
            gnDivisionList = gnDivisionDAO.getGNDivisionNamesByDsDivisionId(userDS.getId(), language, user);
            gnDivisionList.remove(AppConstants.ALL);
            if (AppConstants.ENGLISH.equals(language)) {
                dsDivisionName = userDS.getCode() + " : " + userDS.getEnDivisionName();
                districtName = userDS.getDistrict().getCode() + " : " + userDS.getDistrict().getEnDistrictName();

            } else if (AppConstants.SINHALA.equals(language)) {
                dsDivisionName = userDS.getCode() + " : " + userDS.getSiDivisionName();
                districtName = userDS.getDistrict().getCode() + " : " + userDS.getDistrict().getSiDistrictName();

            } else if (AppConstants.TAMIL.equals(language)) {
                dsDivisionName = userDS.getCode() + " : " + userDS.getTaDivisionName();
                districtName = userDS.getDistrict().getCode() + " : " + userDS.getDistrict().getTaDistrictName();

            }

        }
    }


    public Map<Integer, String> getGnDivisionList() {
        return gnDivisionList;
    }

    public void setGnDivisionList(Map<Integer, String> gnDivisionList) {
        this.gnDivisionList = gnDivisionList;
    }

    public List<Schedule> getScheduleList() {
        return scheduleList;
    }

    public void setScheduleList(List<Schedule> scheduleList) {
        this.scheduleList = scheduleList;
    }

    public String getDistrictName() {
        return districtName;
    }

    public void setDistrictName(String districtName) {
        this.districtName = districtName;
    }

    public String getDsDivisionName() {
        return dsDivisionName;
    }

    public void setDsDivisionName(String dsDivisionName) {
        this.dsDivisionName = dsDivisionName;
    }

    public long[] getScheduleLandList() {
        return scheduleLandList;
    }

    public void setScheduleLandList(long[] scheduleLandList) {
        this.scheduleLandList = scheduleLandList;
    }

    public int getGnDivisionId() {
        return gnDivisionId;
    }

    public void setGnDivisionId(int gnDivisionId) {
        this.gnDivisionId = gnDivisionId;
    }

    public Schedule getSchedule() {
        return schedule;
    }

    public void setSchedule(Schedule schedule) {
        this.schedule = schedule;
    }

    public int getScheduleId() {
        return scheduleId;
    }

    public void setScheduleId(int scheduleId) {
        this.scheduleId = scheduleId;
    }
}
